
#include "../include/GPCompute.h"

#include <windows.h>
#include <libpngIO.h>

#define SCREEN_WIDTH		512		// texture is 512w
#define SCREEN_HEIGHT		256		// texture is 256h

#define TEXTURE_WIDTH		512
#define TEXTURE_HEIGHT		256

#define F					(0.5f / 256.0f)

void DoCounter( HWND hWnd ) {
	static DWORD t;
	static LONG count;
	DWORD currtime = GetTickCount();
	if ( t==0 ) t = currtime;
	if ( t + 1000 < currtime ) { static int i;
		static char wndtxt[260], strfps[260];
		if (i==0) { i=1; GetWindowTextA( hWnd, wndtxt, MAX_PATH ); }
		wsprintfA( strfps, "%s - FPS:%d", wndtxt, count*1000/(currtime-t) );
		SetWindowTextA( hWnd, strfps );
		t += 1000; count = 0;
	}
	++count;
}

HRESULT LoadShader( LPDWORD* pdwShader, const char* filename )
{
	HANDLE p, f;
	p = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == p ) {
		return E_FAIL;
	}
	f = CreateFileMappingA( p, NULL, PAGE_READONLY, 0, 0, NULL );
	if ( NULL == f ) {
		return E_FAIL;
	}
	*pdwShader = (LPDWORD)MapViewOfFile( f, FILE_MAP_READ, 0, 0, 0 );
	if ( NULL == *pdwShader ) {
		return E_FAIL;
	}
	CloseHandle( f );
	CloseHandle( p );
	return S_OK;
}
void UnloadShader( DWORD* pdwShader )
{
	UnmapViewOfFile( (LPCVOID)pdwShader );
}

float wr=1.0f, hr=1.0f;
int delta = 1;
int ratio = 1;

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_TIMER:
			if ( ratio == 15 ) delta = -1;
			if ( ratio ==  1 ) delta =  1;

			ratio += delta;
			wr = hr = 1.0f / ratio; /**/
			return 0;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE _1, LPSTR _2, INT cmdShow )
{
	HGPGPU hgpgpu;
	HWND hWnd; GP_CONTEXT ctx; RECT rect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, };

	// Register the window class
    WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, /*hInst*/NULL, NULL, NULL, NULL, NULL,
                      "GP Tutorial", NULL };
	(void*)(_1, _2);
	wc.hInstance = hInst; 
    RegisterClassExA( &wc );

    // Create the application's window
    hWnd = CreateWindowA( "GP Tutorial", "GP Tutorial 01: MosaicEffect",
                      WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                      GetDesktopWindow(), NULL, wc.hInstance, NULL );

	// adjust window size ... 
	AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, FALSE );
	MoveWindow( hWnd, 300+rect.left, 300+rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );

	// Prepare the context : 
	ctx.width = SCREEN_WIDTH;
	ctx.height = SCREEN_HEIGHT;
	ctx.colortype = TYPE_COLORALPHA;
	ctx.colorprecision = PREC_IN_32;
	ctx.depthtype = TYPE_DEPTH;
	ctx.depthprecision = PREC_IN_16;
	ctx.canvas = (void*)hWnd;

    // Initialize Direct3D
    if( GpComputeInit( &hgpgpu, &ctx, 0 ) >= 0 )
    {
        MSG msg;
		float fpos1[16];
		float fpos2[] = {
			-1.0f,  1.0f, 0.0f, 1.0f, 
			 1.0f,  1.0f, 0.0f, 1.0f, 
			-1.0f, -1.0f, 0.0f, 1.0f, 
			 1.0f, -1.0f, 0.0f, 1.0f, 
		};
		float fpos3[16];
		float fpos4[] = {
			 0.0f+F/2,  0.0f+F, 0.0f, 1.0f, 
			 1.0f+F/2,  0.0f+F, 0.0f, 1.0f, 
			 0.0f+F/2,  1.0f+F, 0.0f, 1.0f, 
			 1.0f+F/2,  1.0f+F, 0.0f, 1.0f, 
		};
		float depth[] = { 0.0f, 0.0f, 0.0f, 0.0f, };
		float wndex[] = { 0.0f, 1.0f, 2.0f, 3.0f, };

		int ret, i, j; PNGStruct pnginfo;
		HGPARRAY harray; void* data; LPBYTE pData, pSrc; long pitch;
		HGPVRRAY hvrray1, hvrray2; 
		DWORD vshid, pshid; LPDWORD p;
		GP_CALCSTATE state = {0}; 
		GP_RECT rc; HGPSHAPERECT vrc;

		// load resource 
		ret = LoadPNGFileA( "1xx.png", &pnginfo );
		if ( ret < 0 ) OutputDebugStringA( "Failed Load PNGFile!\n" );

		ret = GpComputeAllocate( hgpgpu, pnginfo.width, pnginfo.height, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &harray );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate Memory!\n" );

		ret = GpComputeGetPointer( harray, &data, &pitch );
		if ( ret < 0 ) OutputDebugStringA( "Failed GetPointer!\n" );

		// copy data to array 
		pData = (LPBYTE)data;
		pSrc = pnginfo.pData;
		for ( i=0; i<pnginfo.height; ++i ) {
		//	memcpy( pData, pSrc, pnginfo.width );
		//	pSrc += pnginfo.width; pData += pitch;
			for ( j=0; j<pnginfo.width; ++j ) {
				unsigned long n = *((DWORD*)pSrc+j);
			//	A R G B				 // A B G R
				*((DWORD*)pData+j) = ((n&0xFF000000))|((n&0x00FF0000)>>16)|((n&0x0000FF00))|((n&0x000000FF)<<16);
			/*	*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;
				*((DWORD*)pData+j) = (0xFF<<24)|(j<<16)|(j<<8)|j, ++j;*/
			}
			pSrc += pnginfo.width*sizeof(DWORD), pData += pitch;
		}
		FreePNGFile( &pnginfo );

		// vertex prepare 
		rc.pt1.x = rc.pt1.y = 0.0f;
		rc.pt2.x = rc.pt2.y = 1.0f;
		ret = GpComputeShapeRectEx( hgpgpu, &rc, depth, wndex, 1, &vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create Vertex Rectangle!\n" );
		ret = GpComputeSelectSourceShape( hgpgpu, vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetStreamSource and Index!\n" );

		// texture prepare 
		ret = GpComputeAllocateVram( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &hvrray1 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory 1!\n" );
		ret = GpComputeAllocateVram( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ|OPMODE_WRITE, &hvrray2 ); // middle ... 
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory 2!\n" );

		ret = GpComputeSubmit( hgpgpu, harray, hvrray1, 0, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );
		ret = GpComputeSubmit( hgpgpu, harray, hvrray1, 256, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );

		// shader programe 
		if (FAILED(LoadShader( &p, "GPComputeMosaic.vso" ))) 
			OutputDebugStringA( "Failed Allocate Load VertexShader!\n" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_VERTEX, &vshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create VertexShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader!\n" );

		if (FAILED(LoadShader( &p, "GPComputeMosaic.pso" )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select PixelShader!\n" );

		// render state : 
		state.red_write = state.green_write = state.blue_write = 1;
		ret = GpComputeSetProp( hgpgpu, &state );
		if ( ret < 0 ) OutputDebugStringA( "Failed Set RenderState!\n" );

		memcpy( fpos1, fpos2, sizeof(fpos1) );
		memcpy( fpos3, fpos4, sizeof(fpos3) );

		// Show the window
		ShowWindow( hWnd, cmdShow );
		UpdateWindow( hWnd );

        // Enter the message loop
        ZeroMemory( &msg, sizeof(msg) );

		SetTimer( hWnd, 0, 100, NULL );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
				GP_PREPARE prepare = {0};
				DoCounter( hWnd );
				// RENDER 

				fpos1[4] = fpos1[12] = 2.0f * wr - 1.0f; // do scale 
				fpos1[9] = fpos1[13] = 1.0f - 2.0f * hr; // do scale 

				fpos3[4] = fpos3[12] = wr; // do scale 
				fpos3[9] = fpos3[13] = hr; // do scale 

				GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 0, fpos1, 4 ); // pos 
				GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 4, fpos4, 4 ); // tex
				ret = GpComputeSelectSourceUnit( hgpgpu, hvrray1, 0 ); 
				if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );

				ret = GpComputeBeginTarget( hgpgpu, hvrray2, &prepare ); // RTT
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 0 );
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );

				GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 0, fpos2, 4 ); // pos
				GpComputeSetConstants( hgpgpu, PROGRAM_VERTEX, 4, fpos3, 4 ); // tex
				ret = GpComputeSelectSourceUnit( hgpgpu, hvrray2, 0 ); // OUTPUT
				if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!" );

			//	prepare.flagmask.color_clear = 1, prepare.value_color = 0xFF0000FF;
				ret = GpComputeBeginTarget( hgpgpu, NULL, &prepare );
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 1 ); // with present
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );
            }
        }
		KillTimer( hWnd, 0 );

		GpComputeDeleteProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		GpComputeDeleteProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		
		GpComputeDeallocateVram( hgpgpu, hvrray2 );
		GpComputeDeallocateVram( hgpgpu, hvrray1 );
		GpComputeDeallocate( hgpgpu, harray );

		GpComputeShapeFree( hgpgpu, vrc );

		GpComputeFree( hgpgpu );
    }

	DestroyWindow( hWnd );
    // Clean up everything and exit the app
    UnregisterClassA( "GP Tutorial", wc.hInstance );
    return 0;
}
